package com.eagle.nenaboot.client.handler;

import com.eagle.nenaboot.client.net.TcpConnection;
import com.eagle.nenaboot.common.protocol.ConnectPacket;
import com.eagle.nenaboot.common.protocol.DataPacket;
import com.eagle.nenaboot.common.protocol.RegisterPacket;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>
 *  客户端连接器
 * <p>
 *
 * @author eagle liu
 * @since 2020/3/13 21:14
 */
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClientHandler extends ChannelInboundHandlerAdapter {

    protected ChannelHandlerContext ctx;

    private ConcurrentHashMap<String, Channel> channelHandlerMap = new ConcurrentHashMap<>();

    /**
     *  令牌
     */
    private String token;

    /**
     *  被代理的本地服务器ip地址
     */
    private String proxyAddress;

    /**
     *  被代理程序端口
     */
    private int proxyPort;

    public ClientHandler(String token, String proxyAddress, int proxyPort) {
        this.token = token;
        this.proxyAddress = proxyAddress;
        this.proxyPort = proxyPort;
    }

    /**
	 * 第一步: 连接10240服务端。发送RegisterPacket
     * <p>
     *  注册消息到服务端
     * <p>
     *
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx){
		System.out.println("第一步:客户端接入");
        RegisterPacket registerPacket = new RegisterPacket();
        registerPacket.setToken(token);
        ctx.writeAndFlush(registerPacket);
        this.ctx = ctx;
    }

    /**
	 * 第四步: 10240客户端收到RegisterPacket
	 * 第六步: 接收到80服务端发过来的ConnectPacket
	 *
	 * 第八步: 接收到80服务端发过来的DataPacket,并发起一个代理端口（比如3306）的数据写入
	 *
     * <p>
     *  数据响应
     * <p>
     *
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg){
        if (msg instanceof RegisterPacket){
            RegisterPacket registerPacket = (RegisterPacket) msg;
            System.out.println("第四步: 收到RegisterPacket"+registerPacket.getMessage());
            if (!registerPacket.getSuccess()){
                ctx.close();
            }
        }else if (msg instanceof ConnectPacket){
			System.out.println("第六步: 接收到80服务端发过来的ConnectPacket,并发起一个代理端口（比如3306）的客户连接");
            ConnectPacket connectPacket = (ConnectPacket) msg;
            TcpConnection connection = new TcpConnection();
            try{
                ClientHandler clientHandler = this;
                //本地发起一个代理端口（比如3306）的客户连接,连接后，提供服务方有应答数据返回(比如mysql)，触发了channelRead
                connection.connect(proxyAddress, proxyPort, new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel channel){
                        LocalProxyHandler localProxyHandler = new LocalProxyHandler(clientHandler, connectPacket.getChannelId());
                        channel.pipeline().addLast(new ByteArrayDecoder(), new ByteArrayEncoder(),
                                localProxyHandler);
                        //connectPacket.getChannelId()是80端口服务端的channelID
                        channelHandlerMap.put(connectPacket.getChannelId(),channel);
                    }
                });
            }catch (Exception e){
                System.out.println("本地代理服务器连接失败");
                channelHandlerMap.remove(connectPacket.getChannelId());
            }
        }if (msg instanceof DataPacket){
			System.out.println("第十步， 接收到80服务端DataPacket，向本地代理端口（比如3306）写入数据，一个请求写流程完毕");
            DataPacket dataPacket = (DataPacket) msg;
			//拿到本地代理端口（比如3306）的客户连接
            Channel channel = channelHandlerMap.get(dataPacket.getChannelId());

            channel.writeAndFlush(dataPacket.getBytes());
//            System.out.println(JSON.toJSONString(dataPacket));
        }
    }
}
